/*
 * Created on May 29, 2014
 * Created by Paul Gardner
 * 
 * Copyright 2014 Azureus Software, Inc.  All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License only.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

package com.aelitis.azureus.core.dht.transport.udp.impl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import com.aelitis.azureus.core.dht.transport.DHTTransportAlternativeContact;
import com.aelitis.azureus.core.dht.transport.DHTTransportAlternativeNetwork;

public class DHTTransportAlternativeNetworkImpl implements DHTTransportAlternativeNetwork {
    private static final int LIVE_AGE_SECS = 20 * 60;
    private static final int LIVEISH_AGE_SECS = 40 * 60;
    private static final int MAX_CONTACTS_PUB = 64;
    private static final int MAX_CONTACTS_I2P = 16;

    private static final boolean TRACE = false;

    private int network;
    private final int max_contacts;

    private TreeSet<DHTTransportAlternativeContact> contacts = new TreeSet<DHTTransportAlternativeContact>(
            new Comparator<DHTTransportAlternativeContact>() {
                public int compare(DHTTransportAlternativeContact o1, DHTTransportAlternativeContact o2) {
                    int res = o1.getAge() - o2.getAge();

                    if (res == 0) {

                        res = o1.getID() - o2.getID();
                    }

                    return (res);
                }
            });

    protected DHTTransportAlternativeNetworkImpl(int _net) {
        network = _net;

        max_contacts = network == AT_I2P ? MAX_CONTACTS_I2P : MAX_CONTACTS_PUB;
    }

    public int getNetworkType() {
        return (network);
    }

    public List<DHTTransportAlternativeContact> getContacts(int max) {
        return (getContacts(max, false));
    }

    protected List<DHTTransportAlternativeContact> getContacts(int max, boolean live_only) {
        if (max == 0) {

            max = max_contacts;
        }

        List<DHTTransportAlternativeContact> result = new ArrayList<DHTTransportAlternativeContact>(max);

        Set<Integer> used_ids = new HashSet<Integer>();

        synchronized (contacts) {

            Iterator<DHTTransportAlternativeContact> it = contacts.iterator();

            while (it.hasNext()) {

                DHTTransportAlternativeContact contact = it.next();

                if (live_only && contact.getAge() > LIVEISH_AGE_SECS) {

                    break;
                }

                Integer id = contact.getID();

                if (used_ids.contains(id)) {

                    continue;
                }

                used_ids.add(id);

                result.add(contact);

                if (result.size() == max) {

                    break;
                }

            }
        }

        if (TRACE) {
            System.out.println(network + ": sending " + result.size() + " contacts");
        }

        return (result);
    }

    private void trim() {
        Iterator<DHTTransportAlternativeContact> it = contacts.iterator();

        int pos = 0;

        while (it.hasNext()) {

            it.next();

            pos++;

            if (pos > max_contacts) {

                it.remove();
            }
        }
    }

    protected void addContactsForSend(List<DHTTransportAlternativeContact> new_contacts) {
        synchronized (contacts) {

            for (DHTTransportAlternativeContact new_contact : new_contacts) {

                if (TRACE)
                    System.out.println(network + ": send: add contact: " + getString(new_contact));

                contacts.add(new_contact);
            }

            if (contacts.size() > max_contacts) {

                trim();
            }

            if (TRACE)
                System.out.println(network + ":     contacts=" + contacts.size());
        }
    }

    protected void addContactFromReply(DHTTransportAlternativeContact new_contact) {
        synchronized (contacts) {

            if (TRACE)
                System.out.println(network + ": recv: add contact: " + getString(new_contact));

            contacts.add(new_contact);

            if (contacts.size() > max_contacts) {

                trim();
            }

            if (TRACE)
                System.out.println(network + ":     contacts=" + contacts.size());
        }
    }

    protected int getRequiredContactCount() {
        synchronized (contacts) {

            int num_contacts = contacts.size();

            int result = 0;

            if (num_contacts < max_contacts) {

                result = max_contacts - num_contacts;

            } else {

                Iterator<DHTTransportAlternativeContact> it = contacts.iterator();

                int pos = 0;

                while (it.hasNext()) {

                    DHTTransportAlternativeContact contact = it.next();

                    if (contact.getAge() > LIVE_AGE_SECS) {

                        result = max_contacts - pos;

                        break;

                    } else {

                        pos++;
                    }
                }
            }

            if (TRACE)
                System.out.println(network + ": required=" + result);

            return (result);
        }
    }

    private String getString(DHTTransportAlternativeContact contact) {
        return (contact.getProperties() + ", age=" + contact.getAge());
    }
}
